#include "frame.h"
#include "base/core/os/debug.h"
#include "base/core/com_define.h"
#include "base/core/os/time.h"
#include "base/core/log.h"

char g_errstr[1024];

Net*  CreateNet() 
{
	return new Frame();
}

void DeleteNet(Net* net) 
{
	MMOLOG_ASSERT(net != NULL);
	delete net;
}

Frame::Frame()
  : Net(),
    sendedLength_(0),
    module_(NULL),
    buff_pool_(NULL),
    session_pool_(NULL),
    init_data_(),
    fixe_timeout_(),
    free_timeout_(),
    connect_list_(),
    handle_link_(),
    handle_linked_() 
{}

Frame::~Frame() 
{}

bool Frame::Init(const NetInit& init_data) 
{
	init_data_    = init_data;
	session_pool_ = new SessionPool(init_data_.max_conn, false);
	MMOLOG_ASSERT(session_pool_);

	buff_pool_ = new RawAllocator();
	MMOLOG_ASSERT(buff_pool_);

	module_ = CreateModule();
	MMOLOG_ASSERT(module_);

	bool retCode = module_->Init(init_data_.max_conn);
	if (!retCode) 
	{
		LogErrPrint("Net module init fail\n");
		return false;
	}

	handle_linked_.Init(session_pool_,
						module_,
						&fixe_timeout_,
						&free_timeout_,
						&connect_list_,
						init_data_.callback);

	handle_link_.Init(buff_pool_,
					  session_pool_,
					  module_,
					  &fixe_timeout_,
					  &connect_list_,
					  reinterpret_cast<Handle*>(&handle_linked_),
					  init_data_.callback);

	return true;
}

void Frame::UnInit() 
{
	handle_link_.SetCallback(NULL);
	handle_linked_.SetCallback(NULL);
 
	WaitSessionClose();

	if (module_) 
	{
		module_->UnInit();
		DeleteModule(module_);
		module_ = NULL;
	}

	for (uint32_t i = 0; i < listen_data_.size(); ++i) 
	{
		if (listen_data_[i] != NULL) 
		{
			Socket::Close(listen_data_[i]->sock);
			delete listen_data_[i];
		}
	}
	listen_data_.clear();

	if (buff_pool_) 
	{
		delete buff_pool_;
		buff_pool_ = NULL;
	}

	if (session_pool_) 
	{
		delete session_pool_;
		session_pool_ = NULL;
	}
	sendedLength_ = 0;
}

bool  Frame::Loop(bool poolWrite, bool poolRead, uint64_t timeout_ms) 
{
	uint64_t tick_ms = Time::Tick();

	handle_link_.UpdateTick(tick_ms);
	handle_linked_.UpdateTick(tick_ms);

	if (poolWrite || poolRead) 
	{
		module_->Poll(poolWrite, poolRead, timeout_ms);
	}
	CheckTimeout(tick_ms);
	return true;
}

bool  Frame::Poll(bool poolWrite, bool poolRead, uint64_t timeout_ms) 
{
	return module_->Poll(poolWrite, poolRead, timeout_ms);
}

int Frame::Close(ISession* isession) 
{
	Session* session = reinterpret_cast<Session*>(isession);
	//session->SetUserData(SESSION_USER_DATA());
	session->Shutdown();
	return 0;
}

bool Frame::CloseListen(void* handle) 
{
    MMOLOG_ASSERT(handle != NULL);

    EventDataEx* event_data = NULL;
    auto iter = listen_data_.begin();
    auto iter_end = listen_data_.end();
    for ( ; iter != iter_end; ++iter) 
	{
        event_data = (*iter);
        if (event_data == (EventDataEx*)handle) 
		{
            MMOLOG_ASSERT(event_data->sock != INVALID_SOCKET);
            module_->DelEvent(event_data->sock, event_data);
            Socket::Close(event_data->sock);
            delete event_data;
            listen_data_.erase(iter);
            return true;
        }
    }
    return false;
}

void* Frame::Listen(const ConnectPeer* peer) 
{
	MMOLOG_ASSERT(peer != NULL);

	SOCKET sock = Socket::Create();
	if (sock == INVALID_SOCKET) 
	{
		return NULL;
	}

	bool retCode = Socket::Listen(sock, peer->ip, peer->port);
	if (!retCode) 
	{
		LogErrFmtPrint("[Net] listen(%s:%u) error", peer->ip, peer->port);
		Socket::Close(sock);
		return NULL;
	}

	EventDataEx* event_data  = new EventDataEx();
	MMOLOG_ASSERT(event_data);
	event_data->sock_flag   = SOCKET_LISTEN;
	event_data->user_req    = peer->req;
	event_data->context     = peer->context;
	event_data->port        = peer->port;
	event_data->sock        = sock;
	event_data->subpackage  = reinterpret_cast<void*>(peer->sub_func);
	event_data->handle      = &handle_link_;
	event_data->sendBufSize = peer->sendBufSize;
	event_data->recvBufSize = peer->recvBufSize;

	retCode = module_->AddEvent(sock, EVENT_READ, event_data);
	if (!retCode) 
	{
		Socket::Close(sock);
		delete event_data;
		return NULL;
	}

	listen_data_.push_back(event_data);
	return event_data;
}

bool Frame::Connect(const ConnectPeer* peer) 
{
	MMOLOG_ASSERT(peer != NULL);
	bool retCode = false;

	SOCKET sock = Socket::Create();
	if (sock == INVALID_SOCKET) 
	{
		init_data_.callback->OnConnect(NULL, peer->req, peer->context, false);
		return false;
	}

	retCode = Socket::Connect(sock, peer->ip, peer->port);
	if (!retCode) 
	{
		Socket::Close(sock);
		init_data_.callback->OnConnect(NULL, peer->req, peer->context, false);
		return false;
	}

	EventDataEx* event_data  = new EventDataEx();
	MMOLOG_ASSERT(event_data);
	event_data->sock_flag   = SOCKET_CONNECT;
	event_data->user_req    = peer->req;
	event_data->context     = peer->context;
	event_data->port        = peer->port;
	event_data->sock        = sock;
	event_data->subpackage  = reinterpret_cast<void*>(peer->sub_func);
	event_data->handle      = &handle_link_;
	event_data->sendBufSize = peer->sendBufSize;
	event_data->recvBufSize = peer->recvBufSize;

	retCode = module_->AddEvent(sock, EVENT_WRITE, event_data);
	if (!retCode) 
	{
		Socket::Close(sock);
		delete event_data;
		init_data_.callback->OnConnect(NULL, peer->req, peer->context, false);
		return false;
	}

	return true;
}

bool Frame::ResetTimeout(ISession* isession, uint32_t tm/* = 0xFFFFFFFF*/) 
{
    Session* s = reinterpret_cast<Session*>(isession);
    MMOLOG_ASSERT(s->IsWorking());

    DoubleNode* node = s->GetListNode();
    if (tm == init_data_.timeout_ms || tm == 0xFFFFFFFF) 
	{
        if (s->IsFreeTimeout()) 
		{
            s->SetTimeout(0xFFFFFFFF);
            free_timeout_.Delete(node);
            fixe_timeout_.InsertTail(node);
        }
    } 
	else 
	{
        if (! s->IsFreeTimeout()) 
		{
            fixe_timeout_.Delete(node);
            free_timeout_.InsertTail(node);
        }
        s->SetTimeout(tm);
    }
    return true;
}

bool Frame::Send(ISession* isession, const char* buf, uint32_t len, bool raw /*= false*/) 
{
	Session* s = reinterpret_cast<Session*>(isession);
	MMOLOG_ASSERT(s->IsWorking());
	bool retCode = s->Send(buf, len, raw);
	if (!retCode) 
	{
		return false;
	}

	sendedLength_ += len;
	EventFlag flag = s->IsSendComplete() ? EVENT_READ : EVENT_RANDW;
	module_->ModEvent(s->GetSocket(), flag, s->GetEventData());
	return true;
}

uint64_t Frame::GetSendedLength() 
{
    return sendedLength_;
}

uint32_t Frame::GetAcceptSessionCount() 
{
    return (fixe_timeout_.Count() + free_timeout_.Count());
}

uint32_t Frame::GetConnectSessionCount() 
{
    return connect_list_.Count();
}

void Frame::CheckTimeout(uint64_t tick_ms) 
{
    CheckFixeTimeout(tick_ms);
    CheckFreeTimeout(tick_ms);
}

void Frame::CheckFixeTimeout(uint64_t tick_ms) 
{
	Session*    session = NULL;
	DoubleNode* curr    = fixe_timeout_.Head();

	while (curr) 
	{
		session = reinterpret_cast<Session*>(curr->GetHost());
		MMOLOG_ASSERT(!session->IsFreeTimeout());
		if (session->GetActiveTime() + init_data_.timeout_ms < tick_ms) 
		{
			curr = Timeout(curr);
		} 
		else 
		{
			return;
		}
	}
}

void Frame::CheckFreeTimeout(uint64_t tick_ms) 
{
	Session*    session = NULL;
	DoubleNode* curr    = free_timeout_.Head();

	while (curr) 
	{
		session = reinterpret_cast<Session*>(curr->GetHost());
		MMOLOG_ASSERT(session->IsFreeTimeout());
		if (session->IsTimeout(tick_ms)) 
		{
			curr = Timeout(curr);
		} 
		else 
		{
			curr = curr->next;
		}
	}
}

DoubleNode* Frame::Timeout(DoubleNode* node) 
{
	DoubleNode* next    = node->next;
	Session*    session = reinterpret_cast<Session*>(node->GetHost());

	handle_linked_.Timeout(session);
	return next;
}

void Frame::WaitSessionClose() 
{
    ShutdownSession(&fixe_timeout_);
    ShutdownSession(&free_timeout_);
    ShutdownSession(&connect_list_);

    uint32_t times = 0;
    while ((! fixe_timeout_.IsEmpty()
         || ! free_timeout_.IsEmpty()
         || ! connect_list_.IsEmpty())
         && times < 500) 
	{
		Loop(true, true, 20);
		++times;
    }

    ForceCloseSession(&fixe_timeout_);
    ForceCloseSession(&free_timeout_);
    ForceCloseSession(&connect_list_);
}

void  Frame::ShutdownSession(DoubleList* session_list) 
{
	if ((session_list != NULL) && (! session_list->IsEmpty())) 
	{
		Session*    session = NULL;
		DoubleNode* curr = session_list->Head();

		while (curr != NULL) 
		{
			session = reinterpret_cast<Session*>(curr->GetHost());
			if (session != NULL)
			{
				session->Shutdown();
			}
			curr = session_list->Next(curr);
		}
	}
}

void Frame::ForceCloseSession(DoubleList* session_list) 
{
    if ((session_list != NULL) && (! session_list->IsEmpty())) 
	{
        Session*    session = NULL;
        DoubleNode* curr = session_list->Head();

        while (curr != NULL) 
		{
            session = reinterpret_cast<Session*>(curr->GetHost());
            if (session != NULL) 
			{
                module_->DelEvent(session->GetSocket(), session->GetEventData());
                session->UnInit();
                session_pool_->FreeObj(session);
            }
            curr = session_list->Next(curr);
        }
        session_list->Clear();
    }
}
